#include "stdafx.h"
#include "MemMan.h"

//constructor/destructor//////////////////////////////////////////////////
CMemMan::CMemMan()
{
	Init();
}

CMemMan::~CMemMan()
{
	Clear();
}

//Public//////////////////////////////////////////////////////////////////
unsigned CMemMan::GetEventsCountForThread(unsigned threadIdx, bool useFilter)
{
	if(useFilter && _filteredThreadIdx == threadIdx)//If filtered, return size of filtered thread
#ifdef _X64
		return (unsigned)(_threadFilter->size());
#else
		return _threadFilter->size();
#endif
#ifdef _X64
	return (unsigned)(globalCollection->at(threadIdx)->size());
#else
	return globalCollection->at(threadIdx)->size();
#endif
}

unsigned CMemMan::GetThreadIndex(short item)
{
#ifdef _X64
	unsigned idx = (unsigned)(threads->size());
#else
	unsigned idx = threads->size();
#endif
	if (idx)
	{
		vector<short>::const_reverse_iterator iter;
		for (iter = threads->rbegin(); iter != threads->rend(); iter++)
		{
			idx--;
			if(*iter == item)
				return idx;
		}
	}
	threads->push_back(item);
	globalCollection->push_back(new vector<line>);
#ifdef _X64
	return (unsigned)(threads->size() - 1);
#else
	return threads->size() - 1;
#endif
}

short CMemMan::GetTidForIndex(unsigned threadIdx)
{
	return threads->at(threadIdx);
}

unsigned CMemMan::GetThreadsCount()
{
#ifdef _X64
	return (unsigned)(threads->size());
#else
	return threads->size();
#endif
}

unsigned CMemMan::GetServicesCount()
{
#ifdef _X64
	return (unsigned)(srvs->size());
#else
	return srvs->size();
#endif
}

char* CMemMan::GetEventData(unsigned threadIdx, unsigned eventIdx, Collections colId, bool useFilter)
{
	if(useFilter && _filteredThreadIdx == threadIdx) //If filtered, retutn data for filtered index
		eventIdx = _threadFilter->at(eventIdx);
	line *l = &(globalCollection->at(threadIdx)->at(eventIdx));
	switch (colId)
	{
		case CMemMan::Message:
			return l->Msg;
		case CMemMan::Time:
			return (char*)(l->Time);
		case CMemMan::Service:
			return srvs->at(l->Srv);
		case CMemMan::Type:
			return (char*)(l->Type);
		default:
			return NULL;
	}
}

char* CMemMan::GetServiceById(unsigned idx)
{
	return srvs->at(idx);
}

void CMemMan::PushLine(char* data)
{
	if (data && *data)
	{
		if (*data == 9 || *data == 32 || *(data + 12) != 9)
			return;
		line aLine;

		aLine.Time = GetTimeFast(data);
		
		char *np = data + 13;
		short tid = Hex2Short(&np);//KL logs have different hex styles.
		//Here we at the same time change the np according to the format of hex.

		bool isWidget = false;
		if(*np == 0x30)
		{
			np +=2;
			isWidget = true;
		}

		aLine.Type = GetTypeFast(np); 
		np += 4;

		char* ep = np + 1;
		while(*ep && *ep != 9) 
			ep++; //Second isWidget test: if no tabs occur before 1st \r\n - assume widget mode as well
		if(!*ep)
			isWidget = true;

		if(isWidget)
		{
			aLine.Srv = GetCharPtrColItemIndex("n/a", 3, srvs);
		}
		else
		{
			aLine.Srv = GetCharPtrColItemIndex(np, ep - np, srvs);
			np = ep + 1;
		}

		aLine.Msg = PackLine(np);
		globalCollection->at(GetThreadIndex(tid))->push_back(aLine);
	}
}

unsigned CMemMan::PushBulk(char* data, unsigned length)
{
	char *sp, *ep;
	sp = data;
	ep = sp;
	while(ep < data + length)
	{
		if(*ep == 0xA)
		{
			if(*(ep - 1) == 0xD) 
				*(ep - 1) = 0;
			*ep = 0;
			PushLine(sp);
			sp = ep + 1;
		}
		ep++;
	}
#ifdef _X64
	return (unsigned)(ep - sp);
#else
	return ep - sp;
#endif
}

void CMemMan::AddFilter(char* data, Collections colId)
{
	char typeId = 0;
	switch(colId)
	{
		case CMemMan::Type:
			if(!_typeFilter)
				_typeFilter = new vector<char>();
			typeId = GetTypeFast(data);
			if(!CharColContainsItem(typeId, _typeFilter))
				_typeFilter->push_back(typeId);
			break;
		case CMemMan::Service:
			if(!_serviceFilter)
				_serviceFilter = new vector<char*>();
			//Same as above - search for item in collection and add if not found
			GetCharPtrColItemIndexEx(data, strlen(data), _serviceFilter);
			break;
		default:
			throw new exception("Invalid colId value passed!");
			break;
	}
}

void CMemMan::ClearFilter()
{
	if(_typeFilter)
	{
		delete _typeFilter;//Value typed, cleanup not required 
		_typeFilter = NULL;
	}
	DeleteCharPtrCol(&_serviceFilter); //This will take care of all needed
	if(_threadFilter)
	{
		delete _threadFilter;//Value typed, cleanup not required 
		_threadFilter = NULL;
	}
	_filteredThreadIdx = 0xFFFFFFFF;
}

void CMemMan::ApplyFilter(unsigned threadIdx)
{
	if(!_threadFilter)
		_threadFilter = new vector<unsigned>();
	_threadFilter->clear();
	vector<line>::const_iterator iter;
	vector<line> *thread = globalCollection->at(threadIdx);
	unsigned idx = 0;
	for(iter = thread->begin(); iter != thread->end(); iter++)
	{
		line l = *iter;
		char* service = srvs->at(l.Srv);
		if((!_typeFilter || _typeFilter->size() == 0 || CharColContainsItem(l.Type, _typeFilter)) 
		  && (!_serviceFilter || _serviceFilter->size() == 0 || CharPtrColContainsItem(service, strlen(service), _serviceFilter)))
			_threadFilter->push_back(idx);
		idx++;
	}
	_filteredThreadIdx = threadIdx;
}

unsigned CMemMan::GetFilteredThreadIdx()
{
	return _filteredThreadIdx;
}

//Private////////////////////////////////////////////////////////////////
void CMemMan::Init()
{
	globalCollection = new vector<vector<line>*>();
	srvs = new vector<char*>();
	threads = new vector<short>();
	_heap = GetProcessHeap();
	_msgHeap = HeapCreate(0, 0, 0);
	for(int i = 0; i < 256; i++)
		for(int j = 0; j < 256; j++)
			dictionary[i][j] = NULL;
	_threadFilter = NULL;
	_serviceFilter = NULL;
	_typeFilter = NULL;
	_filteredThreadIdx = 0xFFFFFFFF;
}

void CMemMan::Clear()
{
	ClearFilter();
	if(globalCollection) 
	{
		for(vector<vector<line>*>::reverse_iterator iter = globalCollection->rbegin(); iter != globalCollection->rend(); iter++)
		{
			if (*iter)
			{
				for(vector<line>::reverse_iterator  iter2 = (*iter)->rbegin(); iter2 != (*iter)->rend(); iter2++)
					HeapFree(_msgHeap, 0, (*iter2).Msg);
				delete *iter;
			}
		}
		delete globalCollection;
		globalCollection = NULL;
	}
	if(threads) 
	{
		delete threads;
		threads = NULL;
	}
	DeleteCharPtrCol(&srvs);
	for(int i = 0; i < 256; i++)
		for(int j = 0; j < 256; j++)
			DeleteCharPtrCol(&(dictionary[i][j]));
	HeapDestroy(_msgHeap);
}

void CMemMan::DeleteCharPtrCol(vector<char*>** vect)
{
	if((*vect) && (*vect)->size() > 0)
	{
		for(vector<char*>::iterator iter = (*vect)->begin(); iter != (*vect)->end(); iter++)
			HeapFree(_heap, 0, *iter);
		delete (*vect);
		*vect = NULL;
	}
}

char CMemMan::GetCharPtrColItemIndex(char* item, size_t sz, vector<char*>* vect)
{
	return (char)(GetCharPtrColItemIndexEx(item, sz, vect));
}

//Searches for item in vect collection and adds it if not found. Returns item index
short CMemMan::GetCharPtrColItemIndexEx(char* item, size_t sz, vector<char*>* vect)
{
	unsigned idx = CharPtrColContainsItem(item, sz, vect);
	if (idx)	//e.g. if contains
		return (short)(idx - 1);
	char* stg = (char*)HeapAlloc(_heap, 0, sz + 1);
	memcpy(stg, item, sz);
	stg[sz] = 0;
	vect->push_back(stg);
	return (short)(vect->size() - 1);
}

//Return POSITION of item (index+1). Null means NotFound.
unsigned CMemMan::CharPtrColContainsItem(char* item, size_t sz, vector<char*>* vect)
{
#ifdef _X64
	unsigned idx = (unsigned)(vect->size());
#else
	unsigned idx = vect->size();
#endif
	if (idx)
	{
		vector<char*>::const_reverse_iterator iter;
		for (iter = vect->rbegin(); iter != vect->rend(); iter++)
		{
			if(strlen(*iter) == sz && !memcmp(*iter, item, sz))
				break;
			idx--;
		}
	}
	return idx;
}

//Return POSITION of item (index+1). Null means NotFound.
unsigned CMemMan::CharColContainsItem(char item, vector<char>* vect)
{
#ifdef _X64
	unsigned idx = (unsigned)(vect->size());
#else
	unsigned idx = vect->size();
#endif
	if (idx)
	{
		vector<char>::const_reverse_iterator iter;
		for (iter = vect->rbegin(); iter != vect->rend(); iter++)
		{
			if(*iter == item)
				break;
			idx--;
		}
	}
	return idx;
}

char CMemMan::GetTypeFast(char* ptr)
{
	/*	IMP	0	ERR	2	ALW	4	ANY	7
		INF	1	WRN	3	DBG	5	NON	8
		NTF	1	DNG	3	PRN	6	CRT	9*/
	switch (*ptr)
	{
	case 'I':
		if (ptr[1] == 'M' && ptr[2] == 'P')	
			return 0;
		if (ptr[1] == 'N' && ptr[2] == 'F')	
			return 1; //Kis'13: INF=NTF
	case 'N':
		if (ptr[1] == 'T' && ptr[2] == 'F')	
			return 1;
		if (ptr[1] == 'O' && ptr[2] == 'N')	
			return 8; //Kis'14
	case 'E':
		if (ptr[1] == 'R' && ptr[2] == 'R')	
			return 2;
	case 'W':
		if (ptr[1] == 'R' && ptr[2] == 'N')	
			return 3;//KIS 2013: WRN=DNG
	case 'D':	//KIS 2013: "DBG" added
		if (ptr[2] == 'G')
		{
			if(ptr[1] == 'N')
				return 3;
			if(ptr[1] == 'B')
				return 5;
		}
	case 'A':
		if (ptr[1] == 'L' && ptr[2] == 'W')
			return 4; 
		if (ptr[1] == 'N' && ptr[2] == 'Y')
			return 7; //Kis'13
	case 'P':
		if (ptr[1] == 'R' && ptr[2] == 'N')
			return 6; //Kis'13
	case 'C':
		if (ptr[1] == 'R' && ptr[2] == 'T')
			return 9; //Kis'14
	}
#if _DEBUG
	wchar_t msg[255];
	swprintf(msg, 255, L"MM-DBG: unknown type discovered: %c%c%c\r\n", ptr[0], ptr[1], ptr[2]);
	OutputDebugString(msg);
#endif
	return 255;
}

short CMemMan::Hex2Short(char** hexStr)
{
	short res = 0;
	char* hex = *(hexStr);
	while (*hex != 9)
	{
		res *= 16;
		if(*hex>=0x30 && *hex<0x3A)		//0-9
			res += (*hex - 0x30);
		else if(*hex>0x40 && *hex<0x47) //A-F
			res += (*hex - 0x37);
		else if(*hex>0x60 && *hex<0x67) //a-f
			res += (*hex - 0x57);
		else if(*hex != 'x')			//Kis'13: 0xffff-style
			break;
		hex++;
	}
	*(hexStr) = ++hex;
	return res;
}

int CMemMan::GetTimeFast(char* ptr)
{
	int t = 0;
	for (int i = 0; i < 3; i++)
	{
		t |= ((*ptr - 0x30) * 10 + (*(ptr + 1) - 0x30)) << (i ? (i == 1 ? 16 : 10) : 24); //hour, minute, second
		ptr +=3;
	}
	t |= ((*ptr - 0x30) * 100 + (*(ptr + 1) - 0x30) * 10 + (*(ptr + 2) - 0x30)); //milliseconds - lower 9 bits
	return t;
}

char* CMemMan::PackLine(char* src)
{
	SIZE_T len = strlen(src);
	char* msgStg = (char*)HeapAlloc(_heap, 0, len + 1); //+1 because we add ending null
	char *sp, *ep, *mp;
	sp = src;		//lexem start ptr
	ep = sp;		//lexem end ptr
	mp = msgStg;	//msgStg current position ptr
	SIZE_T llen = 0;//lexem length, used below as total packed content length, too; SIZE_T used to disable x64 warnings
	while(*ep)		//== while (ep - src <= len)
	{
		while(*ep && (*ep != ' ') && (*ep != '/') && (*ep != '\\'))
			ep++;
		llen = ep - sp;
		if(llen > 5)									//there's a sense to pack lexem
		{
			vector<char*>* dict = GetDictionary(*sp, *(sp + 1));
			short idx = GetCharPtrColItemIndexEx(sp + 2, ep - (sp + 2), dict);
			*mp = 1;					//packaging symbol + collection identifiers
			*(mp + 1) = *sp;			
			*(mp + 2) = *(sp + 1);		//i.e. 2 1st letters of lexem
			*(mp + 3) = (char)(idx >> 8);//colection packed index - 4th & 5th chars
			*(mp + 4) = (char)(idx & 0xFF);
			*(mp + 5) = *ep;			//delimeter
			mp += 6;
		}
		else
		{
			memcpy(mp, sp, llen + 1);	//simply copying lexem and delimeter
			mp += (llen + 1);			//and moving msgStg pointer forwards
		}
		if(*ep)							//if not end of line
		{
			ep++;
			sp = ep;
		}
	}
	if(*(mp - 1))
		*(mp - 1) = 0;					//if last char in line is not null separator
	llen = mp - msgStg;					//total compressed length
	char* msgStgMsgHeap = (char*)HeapAlloc(_msgHeap, 0, llen);
	memcpy(msgStgMsgHeap, msgStg, llen);
	HeapFree(_heap, 0, msgStg);
	return msgStgMsgHeap;
}

void CMemMan::ExpandEventMessage(char* msg, char* buffer)
{
	while(true)
	{
		if(*msg == 1)
		{
			*buffer = *(msg + 1);
			*(buffer + 1) = *(msg + 2);
			char* msgContent = dictionary[*(msg + 1)][*(msg + 2)]->at((*(msg + 3)) * 256 + (*(msg + 4)));
			SIZE_T msgContentLength = strlen(msgContent); 
#pragma warning (disable: 4996)		//No security problem: log line cannot be more than 4k :)
			strcpy(buffer + 2, msgContent);
#pragma warning (default: 4996)
			msg += 5;
			buffer += (msgContentLength + 2);
		}
		else
		{
			*buffer = *msg;
			if(*msg)
			{
				buffer++;
				msg++;
			}
			else
				break;
		}
	}
}

void CMemMan::ExpandEventTime(int time, char* buffer)
{
	int t = time >> 24; //byte 1 - hours
	*buffer = 0x30 + (int)(t / 10);
	*(++buffer) = 0x30 + t % 10;
	*(++buffer) = ':';
	t = (time >> 16) & 0xFF; //byte 2 - minutes
	*(++buffer) = 0x30 + (int)(t / 10);
	*(++buffer) = 0x30 + t % 10;
	*(++buffer) = ':';
	t = ((time >> 10) & 0x3F);//bits 1-6 of byte 3 - seconds
	*(++buffer) = 0x30 + (int)(t / 10);
	*(++buffer) = 0x30 + t % 10;
	*(++buffer) = '.';
	t = time & 0x3FF; //Last 10 bits - milliseconds
	*(++buffer) = 0x30 + (int)(t / 100);
	*(++buffer) = 0x30 + (int)((t % 100) / 10);
	*(++buffer) = 0x30 + t % 10;
	*(++buffer) = 0;
}

vector<char*>* CMemMan::GetDictionary(char one, char two)
{
	if(!(dictionary[one][two]))
		dictionary[one][two] = new vector<char*>;
	return dictionary[one][two];
}
